class ReadOnly(Exception):
    """This Node is read only."""
    pass

class ChildNotFound(Exception):
    """The child is not found in children."""
    pass
    
class InvalidRootOpreation(Exception):
    """Invalid operation to root"""
    pass

class Node:
    """Base class Node in DocBlocks.
    
    All widgets will inherit this class 
    """
    
    def __init__(self):
        # Parent of this node
        self.Parent = None
        
        # ID of this node
        self.ID = None
        
        # Owner Doc of this node
        self.Doc = None
        
        # Node's data 
        self.Data = {'version': '1.0'}
        
    def __deepcopy__(self, memo):
        from copy import deepcopy

        NewNode = self.__class__()
        memo[id(self)] = NewNode

        NewNode.Data = deepcopy(self.Data)
        return NewNode
        
    def Modify(self, **Kwargs):
        """Modify this Node with some args.
        
        If this node is read only, raise ReadOnly exception
        
        Kwargs: Args to modify this node
        """
        raise ReadOnly
    
    def Release(self):
        """This function will be called if this node be deleted, for releasing resource"""
        pass
    
    def RegisterCheck(self):
        """Check that did this Node register.
        
        If this Node did not check, raise assert exception
        """
        assert self.ID != None
        assert self.Doc != None
        
    def DeleteFromParent(self, Unregister = True):
        """Delete this node from its parent
        
        Unregister: Should Unregister this Child?
        """
        if self.Parent == None:
            raise InvalidRootOpreation
        self.Parent.Delete(self, Unregister)
        
    def InsertSiblingBefore(self, Child, Register = True):
        """Insert a Sbling child before self
        
        Target: Target to insert child before. If Target is None, push child to back of children
        
        return: Child
        """
        if self.Parent == None:
            raise InvalidRootOpreation
           
        return self.Parent.InsertBefore(self, Child, Register)
    
    def MoveSiblingBefore(self, Child):
        """Move a sibling before self
        
        Target: Target to move before. If Target is None, move child to end of children
        Child: Child to move
        """
        if self.Parent == None:
            raise InvalidRootOpreation
           
        return self.Parent.MoveBefore(self, Child)
    
    def CopySiblingBefore(self, Child):
        """Copy a sibling before self
        
        Target: Target to move before. If Target is None, move child to end of children
        Child: Child to move
        """
        if self.Parent == None:
            raise InvalidRootOpreation
           
        return self.Parent.CopyBefore(self, Child)
    
class Block(Node):  
    """Blcok class for contain Nodes and Blocks in DocBlocks."""
      
    def __init__(self):
        Node.__init__(self)
        self.Children = []
        
    def __deepcopy__(self, memo):
        from copy import deepcopy

        NewBlock = Node.__deepcopy__(self, memo)
        NewBlock.Children = deepcopy(self.Children)
            
        for Child in NewBlock.Children:
            Child.Parent = NewBlock

        return NewBlock
    
    def Release(self):
        """Release all child"""
        for Child in self.Children:
            Child.Release()
    
    def InsertBefore(self, Target, Child, Register = True):
        """Insert a child before target
        
        Target: Target to insert child before. If Target is None, push child to back of children
        Child: Child to insert
        
        return: Child
        """
        if Register:
            self.Doc.Register(Child)
        else:
            Child.RegisterCheck()
        Child.Parent = self
        # Push back
        if Target == None:
            self.Children.append(Child)
        # Insert before
        else:
            # This is not root
            assert Target.Parent != None
            Index = self.GetChildIndex(Target)
            self.Children.insert(Index, Child)
            
        return Child
    
    def MoveBefore(self, Target, Child):
        """Move a child before target
        
        Target: Target to move before. If Target is None, move child to end of children
        Child: Child to move
        """
        Child.RegisterCheck()
        # Delete Child from its parent
        Child.Parent.Delete(Child, False)
        # Insert child before Target
        self.InsertBefore(Target, Child, False)
        
    def CopyBefore(self, Target, Child):
        """Copy a child before target
        
        Target: Target to copy before. If Traget is None, copy child to end of children
        Child: Child to copy
        
        return: New child
        """
        from copy import deepcopy
        
        Child.RegisterCheck()
        NewChild = deepcopy(Child)
        # registe new child to Doc
        self.Doc.Register(NewChild)
        # Insert child before target
        self.InsertBefore(Target, NewChild, False)
        return NewChild
        
    def Delete(self, Child, Unregister = True):
        """Delete a child from children
        
        Child: Child to delete
        Unregister: Should Unregister this Child?
        """
        Child.RegisterCheck()
        Child.Parent = None
        Index = self.GetChildIndex(Child)
            
        del self.Children[Index]
        if Unregister == True:
            self.Doc.Unregister(Child)
            Child.Release()
        
    def GetChildIndex(self, Child):
        """Get index of child in children
        
        Child: Child to get Index
        """
        for i in range(len(self.Children)):
            if Child is self.Children[i]:
                return i
            
        raise ChildNotFound

class Doc:
    """Document class of DocBlocks, contain all document's information."""
    
    def __init__(self):
        # Version of this Doc
        self.Version = '1.0'
        
        # Last ID of Nodes
        self.LastID = 0
        
        # Map for all Nodes
        self.NodeMap = {}
        
        # Root of Doc
        self.Root = None
        
    def Create(self, RootBlock):
        """Create this doc"""
        
        self.LastID = 0
        self.NodeMap = {}
        self.Root = RootBlock()
        self.Root.Parent = None
        self.Register(self.Root)
        
    def __getitem__(self, Index):
        """Shortcut for accessing NodeMap"""
        return self.NodeMap[Index]
        
    def Register(self, Child):
        """Register a new Node object in doc
        
        Child: Child to register
        
        return: Child
        """
        Child.Doc = self
        Child.ID = self.LastID
        self.NodeMap[Child.ID] = Child
        self.LastID += 1
        # Child have children
        if hasattr(Child, 'Children'):
            for ChildOfChild in Child.Children:
                self.Register(ChildOfChild)
                
        return Child
        
    def Unregister(self, Child):
        """Unregister a Node object in doc.
        
        Child: Child to unregister
        
        return: Child
        """
        del self.NodeMap[Child.ID]
        Child.Doc = None
        Child.ID = None
        # Child have children
        if hasattr(Child, 'Children'):
            for ChildOfChild in Child.Children:
                self.Unregister(ChildOfChild)
                
        return Child
    
    def CleanUnlinked(self):
        """Clean those Node that have no parent."""
        NoParent = []
        for i in self.NodeMap:
            Node = self.NodeMap[i]
            # Have no parent and not Root
            if (Node.Parent == None) and (not Node is self.Root):
                NoParent.append(Node)
                 
        for Node in NoParent:
            self.Unregister(Node)
            
    def IsChildOf(self, Child, Parent):
        """Check is this node a child of another node
        
        @param Child: Child to check
        @param Parent: Parent of child  
        """
        if hasattr(Parent, 'Children'):
            if Child in Parent.Children:
                return True
            for ChildOfParent in Parent.Children:
                if self.IsChildOf(Child, ChildOfParent):
                    return True
                
        return False
            
if __name__ == '__main__':
    MyDoc = Doc(Block)
    
    BlockA = MyDoc.Register(Block())
    MyDoc.Root.InsertBefore(None, BlockA)
    
    BlockB = MyDoc.Register(Block())
    MyDoc.Root.InsertBefore(None, BlockB)
    
    print list(MyDoc.Root.Children)
    print list(MyDoc.NodeMap)

    NewBlock = MyDoc.Root.CopyBefore(BlockB, BlockA)
    print list(MyDoc.Root.Children)
    print list(MyDoc.NodeMap)
    
    MyDoc.Root.Delete(NewBlock)
    print list(MyDoc.Root.Children)
    print list(MyDoc.NodeMap)
    
    MyDoc.CleanUnlinked()
    print list(MyDoc.Root.Children)
    print list(MyDoc.NodeMap)
